﻿using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class GraphMgr : MonoBehaviour {
    public static GraphMgr Ins { get; private set; }
    public enum FuncOption {
        Linear,
        Exponential,
        Parabola,
        Sine,
        Ripple,
    }
    [SerializeField]
    private ParticleSystem particle;
    private delegate float FuncDelegate(Vector3 p, float t);
    private static FuncDelegate[] funcDelegates = {
        Linear,
        Exponential,
        Parabola,
        Sine,
        Ripple,
    };

    [Range(10, 100)]
    public int resolution = 10;
    public static float sineLv = .15f;
    private int currentResolution;
    private ParticleSystem.Particle[] points;
    [SerializeField]
    private FuncOption funcOption;

    private void Awake() {
        Ins = this;
    }

    private void Update() {
        if (currentResolution != resolution || points == null) {
            CreatePoints();
        }
        FuncDelegate fn = funcDelegates[(int)funcOption];
        float t = Time.timeSinceLevelLoad;
        for(int i = 0; i < points.Length; i++) {
            var p = points[i].position;
            p.y = fn(p, t);
            points[i].position = p;
            Color c = points[i].color;
            c.g = p.y;
            points[i].color = c;
        }
        particle.SetParticles(points, points.Length);
    }

    private void CreatePoints() {
        currentResolution = resolution;
        points = new ParticleSystem.Particle[resolution * resolution];

        float increment = 1f / (resolution - 1);
        int i = 0;
        for(int x = 0; x < resolution; x++) {
            for(int z = 0; z < resolution; z++) {
                var p = new Vector3(x * increment, 0, z * increment);
                points[i].position = p;
                points[i].color = new Color(0, p.x + p.z, 0);
                points[i++].size = .1f;
            }
        }
    }

    private static float Linear(Vector3 p, float t) {
        return p.x;
    }

    private static float Exponential(Vector3 p, float t) {
        return p.x * p.x;
    }

    private static float Parabola(Vector3 p, float t) {
        p.x = 2f * p.x - 1f;
        p.z = 2f * p.z - 1f;
        return 1f - p.x * p.x * p.z * p.z;
    }

    private static float Sine(Vector3 p, float t) {
        return 0.50f +
            sineLv * Mathf.Sin(4 * Mathf.PI * p.x + 4 * t) * Mathf.Sin(2 * Mathf.PI * p.z + t) +
            0.10f * Mathf.Cos(3 * Mathf.PI * p.x + 5 * t) * Mathf.Cos(5 * Mathf.PI * p.z + 3 * t) +
            0.15f * Mathf.Sin(Mathf.PI * p.x + 0.6f * t);
    }

    private static float Ripple(Vector3 p, float t) {
        p.x -= 0.5f;
        p.z -= 0.5f;
        float squareRadius = p.x * p.x + p.z * p.z;
        return 0.5f + Mathf.Sin(15f * Mathf.PI * squareRadius - 2f * t) / (2f + 100f * squareRadius);
    }
}
